#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;

// void printSig(sigset_t& set)
// {
//     cout << "pending:";
//     for(int i = 31; i >= 0; i--)
//         if(sigismember(&set, i))
//             cout << "1";
//         else
//             cout << "0";
//     cout << "\n";
// }

// void handler(int sig)
// {
//     cout << "caputer signal:" << sig << endl;
//     // sigset_t set;
//     // sigemptyset(&set);
//     // while(true)
//     // {
//     //     sigpending(&set);
//     //     printSig(set);
//     //     sleep(1);
//     // }
// }

// int main()
// {
//     signal(2, handler); //捕获二号信号

//     sigset_t set, oset;
//     sigemptyset(&set);
//     sigpending(&set);
//     printSig(set);

//     sigfillset(&set);
//     sigprocmask(SIG_SETMASK, &set, &oset); //先阻塞所有信号

//     cout << "pid:" << getpid() << endl;

//     while(true)
//     {
//         sigpending(&set);
//         printSig(set);
//         sleep(1);
//     }
//     return 0;
// }

// 利用17号sigchld信号回收子进程
void handler(int sig)
{
    // //1、采用阻塞等待的方式回收子进程
    // 如果有大量的子进程同时退出，那么信号只能记录最新的一次，最终信号只抵达一次，也就是说你只等待了一次，此时会有大量的僵尸
    // int pid = waitpid(-1, nullptr, 0);
    // if(pid > 0)
    //     cout << "wait success, pid:" << pid << endl;
    // else
    //     cout << "wait fail!" << endl;

    // //改进：循环等待，可以回收所有子进程，只要子进程发送信号，即使很多子进程只抵达一次信号，但是我循环等待可以等待完这次信号结束的所有进程
    // 思路：虽然只抵达一次，但是处理函数内我可以一直等待
    // //缺点：如果子进程永远不结束，那么父进程就永远在这里阻塞
    // while (true)
    // {
    //     int pid = waitpid(-1, nullptr, 0); //有子进程就会一直等
    //     if (pid > 0)
    //         cout << "wait success, pid:" << pid << endl;
    //     else
    //     {
    //         cout << "wait fail!" << endl;
    //         break;
    //     }
    // }

    // 2、采用轮询等待方式
    // 和没有循环的阻塞等待是一样的问题
    // 如果有大量的子进程同时退出，那么信号只能记录最新的一次，最终信号只抵达一次，也就是说你只等待了一次，此时会有大量的僵尸
    // int pid = waitpid(-1, nullptr, WNOHANG);
    // if (pid >= 0) // 成功一直等待
    //     cout << "wait success, pid:" << pid << endl;
    // else
    //     cout << "wait fail!" << endl;
    while (true)
    {
        int pid = waitpid(-1, nullptr, WNOHANG); //主要利用它本次没有等到会返回-1
        if (pid >= 0)
            cout << "wait success, pid:" << pid << endl;
        else
        {
            cout << "wait fail!" << endl;
            break;
        }
    }
}

int main()
{
    signal(17, handler); // 捕获17号信号

    // 3、将默认行为改为忽略
    // 17号信号的默认行为本来就是忽略，为什么还要捕获自定义为忽略，这两个忽略本质没多大区别，
    // 但是SIG_IGN处理的工作多一点，会将pcb那些内核数据结构清理掉
    // 方法一：signal
    // signal(17, SIG_IGN);
    // 方法二：sigaction
    struct sigaction act;
    act.sa_handler = SIG_IGN;
    act.sa_flags = 0;
    sigaction(17, &act, nullptr);

    for (int i = 0; i < 100; i++) // 创建100个子进程
    {
        pid_t pid = fork();
        if (pid == 0)
        {
            cout << "child " << i << " pid:" << getpid() << endl;
            int cnt = 5;
            while (cnt--)
                sleep(1);
            // cout << "child " << i << " pid:" << getpid() << ", over!" << endl;
            exit(0);
        }
    }
    sleep(2);
    cout << "fork end!\n";

    while (true);
    return 0;
}